package com.ykt.common.utils;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * HTTP连接服务的帮助类
 *
 * @author long.hua
 * @version 1.0.0
 * @date 2014年8月28日
 */
public final class HTTPClientUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(HTTPClientUtil.class);

    /**
     * 发送GET类型的HTTP请求，
     *
     * @param url       请求的URL地址
     * @param headerMap 设置Header
     * @return 返回请求连接相应的json字符串
     */
    public static String sendGetRequest(String url, Map<String, String> headerMap) {
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if(httpClient == null){
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }

        // 创建HttpGet请求
        int status = -1;
        String result = null;
        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {

            // 设置请求头部
            httpGet = new HttpGet(url);
            Header[] headers = toHeaders(headerMap);
            if(null != headers && headers.length > 0){
                httpGet.setHeaders(headers);
            }

            // 执行get请求.
            long b = System.currentTimeMillis();
            response = httpClient.execute(httpGet);
            if (null != response && response.getStatusLine() != null ){
                status = response.getStatusLine().getStatusCode();
                if(status== HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    // 获取响应实体
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
            long e = System.currentTimeMillis();

            // 打印响应内容
            LOGGER.info("End request, cost time: {}", (e - b));
            LOGGER.debug("Response content: {}", ((null != result && result.length() > 512) ? result.substring(0, 512) : result));

        } catch (Exception e) {
            LOGGER.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
        } finally {
            closeResource(response, httpGet, httpClient);
        }

        return result;
    }

    /**
     * 发送GET类型的HTTP请求，
     *
     * @param url 请求的URL地址
     * @return 返回请求连接相应的json字符串
     */
    public static String sendGetRequest(String url) {
        return sendGetRequest(url, null);
    }

    /**
     * 发送 post请求访问本地应用并根据传递参数不同返回不同结果
     *
     * @param url      post请求的链接
     * @param paramMap 请求的参数Map
     */
    public static String sendPostRequest(String url, Map<String, Object> paramMap) {
        return sendPostRequest(url, null, paramMap);
    }

    private static Header[] toHeaders(Map<String, String> headerMap){
        if(null == headerMap || headerMap.isEmpty()){
            return null;
        }

        int i = 0;
        Header[] headers = new Header[headerMap.size()];
        for (Entry<String, String> entry : headerMap.entrySet()) {
            if(null != entry.getValue()){
                headers[i++] = new BasicHeader(entry.getKey(), entry.getValue());
            }
        }

        return headers;
    }

    /**
     * 将保存POST请求的参数以及值得键值对mao转换为HTTPClient POST请求需要的List<NameValuePair>类型
     *
     * @param paramMap 请求参数
     */
    private static List<NameValuePair> toNameValuePairList(Map<String, Object> paramMap) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        if (paramMap != null) {
            for (Entry<String, Object> entry : paramMap.entrySet()) {
                Object value = entry.getValue();
                if (value != null) {
                    if (value instanceof String[]) {
                        String[] values = (String[]) value;
                        if (values.length > 0) {
                            for (String str : values) {
                                nvps.add(new BasicNameValuePair(entry.getKey(), str));
                            }
                        }
                    } else {
                        nvps.add(new BasicNameValuePair(entry.getKey(), value.toString()));
                    }
                }
            }
        }

        return nvps;
    }

    public static String sendPostRequest(String url, Map<String, String> headerMap, Map<String, Object> paramMap){
        // 创建默认的httpClient实例.
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if(httpClient == null){
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }

        // 创建httpPost
        int status = -1;
        String result = null;
        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {

            // 设置请求头部
            httpPost = new HttpPost(url);
            Header[] headers = toHeaders(headerMap);
            if(null != headers && headers.length > 0){
                httpPost.setHeaders(headers);
            }

            // 设置请求参数
            List<NameValuePair> valuePairs = toNameValuePairList(paramMap);
            if(null != valuePairs && !valuePairs.isEmpty()){
                httpPost.setEntity(new UrlEncodedFormEntity(valuePairs, "UTF-8"));
            }

            long b = System.currentTimeMillis();
            response = httpClient.execute(httpPost);
            if(null != response && response.getStatusLine() != null){
                status = response.getStatusLine().getStatusCode();
                if(status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED){
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
            long e = System.currentTimeMillis();

            // 打印响应内容
            LOGGER.info("End request, cost time: {}", (e-b));
            LOGGER.debug("Response content: {}", ((null != result && result.length() > 512) ? result.substring(0, 512) : result));

        } catch (Exception e) {
            LOGGER.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
        } finally {
            closeResource(response, httpPost, httpClient);
        }

        return result;
    }

    /**
     * 关闭连接HTTP过程产生的资源
     *
     * @param httpResponse   HTTP响应
     * @param httpClient HTTP客户端
     */
    private static void closeResource(CloseableHttpResponse httpResponse, HttpRequestBase httpMethod, CloseableHttpClient httpClient) {
        if (httpResponse != null) {
            try {
                httpResponse.close();
            } catch (IOException e) {
                LOGGER.error("Close http response error!", e);
            }
        }

        if(null != httpMethod){
            httpMethod.releaseConnection();
        }

        // 关闭连接,释放资源
        if (httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                LOGGER.error("Close http client error!", e);
            }
        }
    }

    private static CloseableHttpClient createSSLInsecureClient() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf =
                    new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
                        public boolean verify(String arg0, SSLSession arg1) {
                            return true;
                        }

                        public void verify(String host, SSLSocket ssl) throws IOException {
                        }

                        public void verify(String host, X509Certificate cert) throws SSLException {
                        }

                        public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                        }
                    });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            LOGGER.error("Create ssl socket factory error!", e);
            return null;
        }
    }

    /**
     * 发送post请求，参数为字符串
     * @param url 请求地址
     * @param post 请求数据
     * @return 返回的内容
     */
    public static String sendPostRequest(String url, String post){
        // 创建默认的httpClient实例.
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if(httpClient == null){
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }

        // 创建httpPost
        int status = -1;
        String result = null;
        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {

            // 设置请求头部
            httpPost = new HttpPost(url);

            // 设置请求参数
            StringEntity stringEntity = new StringEntity(post, ContentType.APPLICATION_JSON);
            httpPost.setEntity(stringEntity);

            // 执行post请求
            long b = System.currentTimeMillis();
            response = httpClient.execute(httpPost);
            if(null != response && response.getStatusLine() != null){
                status = response.getStatusLine().getStatusCode();
                if(status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED){
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
            long e = System.currentTimeMillis();

            // 打印响应内容
            LOGGER.info("End request, cost time: {}", (e-b));
            LOGGER.debug("Response content: {}", ((null != result && result.length() > 512) ? result.substring(0, 512) : result));

        } catch (Exception e) {
            LOGGER.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
        } finally {
            closeResource(response, httpPost, httpClient);
        }

        return result;

    }

    public static void main(String[] args) {
//        String url = "https://api.sandbox.paypal.com/v1/oauth2/token";
//        Map<String, Object> map = new HashMap<String, Object>();
//        map.put("grant_type", "client_credentials");
//	    map.put("Authorization", "AZRfVxBJDsMBr_Mf3nOFG_NnpDBycHaNMUwz_vFMwL6f9BxvGU_-NIjA0laI:EG8DFRDwX5bPBjkxYRk4Gad88bqim7EOh8qYQ9F7NnR9_4nw7JDT0JuU8qB7");
//
//        // 创建默认的httpClient实例.
//        CloseableHttpClient httpClient = createSSLInsecureClient();
//        // 创建httppost
//        HttpPost httpPost = null;
//        CloseableHttpResponse response = null;
//        String respContent = null;
//        // 创建参数队列
//        List<NameValuePair> paramList = toNameValuePairList(map);
//        UrlEncodedFormEntity uefEntity;
//        try {
//            httpPost = new HttpPost(url);
//            uefEntity = new UrlEncodedFormEntity(paramList, "UTF-8");
//            httpPost.setEntity(uefEntity);
//            log.info("executing request : " + httpPost.getURI() + ",current Time:"
//                    + System.currentTimeMillis());
//            CredentialsProvider credsProvider = new BasicCredentialsProvider();
//            credsProvider.setCredentials(
//                    new AuthScope("api.sandbox.paypal.com", AuthScope.ANY_PORT),
//                    new UsernamePasswordCredentials("AZRfVxBJDsMBr_Mf3nOFG_NnpDBycHaNMUwz_vFMwL6f9BxvGU_-NIjA0laI", "EG8DFRDwX5bPBjkxYRk4Gad88bqim7EOh8qYQ9F7NnR9_4nw7JDT0JuU8qB7"));
//            HttpClientContext context = HttpClientContext.create();
//            context.setCredentialsProvider(credsProvider);
//            response = httpClient.execute(httpPost, context);
//            try {
//                HttpEntity entity = response.getEntity();
//                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK && entity != null) {
//                    respContent = EntityUtils.toString(entity, "UTF-8");
//                }
//                log.info("Response content:" + respContent + ",current Time:" + System.currentTimeMillis());
//            } finally {
//                response.close();
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            closeResource(response, httpPost, httpClient);
//        }
//        System.out.println(response);


//        System.out.println(sendGetRequest("https://www.baidu.com/"));

//        Map<String, Object> map=new HashMap<>();
//        map.put("name", "Donald Duck");
//        map.put("city", "Duckburg");
//        System.out.println(sendPostRequest("http://www.w3school.com.cn/example/jquery/demo_test_post.asp",map));

//        long a = System.currentTimeMillis();
//        try {
//            FileUtils.read(new URL("http://apk.aotclouds.net/image/bbb78fcf-f194-476d-a760-904e5fae34af.png").openStream());
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        long b = System.currentTimeMillis();
//        getData("http://apk.aotclouds.net/image/bbb78fcf-f194-476d-a760-904e5fae34af.png");
//        long c = System.currentTimeMillis();
//
//        System.out.println("b-a="+(b-a));
//        System.out.println("c-b="+(c-b));
    }

    /**
     * 通过Get方法请求url获取字节数据
     * @param url 请求地址
     * @return 字节数据，失败为null
     */
    public static byte[] getData(String url){
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if(httpClient == null){
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }

        // 创建HttpGet请求
        int status = -1;
        byte data[] = null;
        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {

            httpGet = new HttpGet(url);
            response = httpClient.execute(httpGet);
            if (null != response && response.getStatusLine() != null){
                status = response.getStatusLine().getStatusCode();
                if (status== HttpStatus.SC_OK || status == HttpStatus.SC_CREATED){
                    // 获取响应实体
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        data = EntityUtils.toByteArray(entity);
                    }
                }
            }
        } catch (IOException e) {
            LOGGER.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
        } finally {
            closeResource(response,httpGet, httpClient);
        }

        return data;
    }
}
